#include <stdarg.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <ifaddrs.h>
#include	<iostream>

#include "NbtUtil.h"

int DEBUGLEVEL = 2;
bool NeedSwap = false;
bool passive = false;
//bool demon = false;

//using namespace NbtUtil;

/*******************************************************************
write an debug message on the debugfile. The first arg is the debuglevel.
********************************************************************/
void Debug(int debug_level, const char *format_str, ...)
{
    va_list ap;
    va_start(ap, format_str);

    if (DEBUGLEVEL >= debug_level)
    {
        vfprintf(stdout,format_str, ap);
        fflush(stdout);
    }

    va_end(ap);
}

bool ip_equal(const struct in_addr *ip1, const struct in_addr* ip2)
{
    return (ip1->s_addr == ip2->s_addr);
}

/*******************************************************************
  byte swap an object - the byte order of the object is reversed
********************************************************************/
void object_byte_swap(void *obj,int size)
{
  int i;
  char c;
  char *p1 = (char *)obj;
  char *p2 = p1 + size - 1;

  size /= 2;

  for (i=0; i<size; i++)
  {
      c = *p1;
      *p1 = *p2;
      *p2 = c;
      p1++;
      p2--;
  }
}

/****************************************************************************
  byte swap a uint16_t
****************************************************************************/
uint16_t uint16_byte_swap(uint16_t x)
{
  uint16_t res;
  res = x;
  object_byte_swap(&res,sizeof(res));
  return(res);
}

/****************************************************************************
  byte swap a uint32_t
****************************************************************************/
uint32_t uint32_byte_swap(uint32_t x)
{
  uint32_t res;
  res = x;
  object_byte_swap(&res,sizeof(res));
  return(res);
}

/*******************************************************************
  true if the machine is big endian
********************************************************************/
bool big_endian(void )
{
  int x = 2;
  char *s;
  s = (char *)&x;
  return(s[0] == 0);
}
/****************************************************************************
  set a value at buf[pos] to integer val
****************************************************************************/
void sival(char *buf,int pos,uint32_t val)
{
  val = ISWP(val);
  memcpy(buf + pos,(char *)&val,sizeof(val));
}

/****************************************************************************
  set a value at buf[pos] to int16 val
****************************************************************************/
void ssval(char *buf,int pos,uint16_t val)
{
  val = SSWP(val);
  memcpy(buf + pos,(char *)&val,sizeof(val));
}

/****************************************************************************
  get a 32 bit integer value
****************************************************************************/
uint32_t ival(const char *buf,int pos)
{
  uint32_t res;
  memcpy((char *)&res,buf + pos,sizeof(res));
  res = ISWP(res);
  return(res);
}


/****************************************************************************
  get a int16 value
****************************************************************************/
uint16_t sval(const char *buf,int pos)
{
  uint16_t res;
  memcpy((char *)&res,buf + pos,sizeof(res));
  res = SSWP(res);
  return(res);
}

void NbtUtil::init()
{
   NeedSwap = !big_endian();
}

/****************************************************************************
find a pointer to a netbios name
****************************************************************************/
const char *NbtUtil::name_ptr(const char *buf,int ofs)
{
  unsigned const char c = *(unsigned const char *)(buf+ofs);

  if ((c & 0xC0) == 0xC0)
  {
      uint16_t l;
      char *p = (char *)&l;
      memcpy(&l,buf+ofs,2);
      p[0] &= ~0xC0;
      l = SVAL(p,0);
      Debug(5,"name ptr to pos %d from %d is %s\n",l,ofs,buf+l);
      return (buf + l);
  }
  else
    return (buf+ofs);
}

/****************************************************************************
extract a netbios name from a buf
****************************************************************************/
void NbtUtil::name_extract(const char *buf,int ofs,char *name)
{
  strcpy(name,"");
  name_interpret(name_ptr(buf,ofs),name);
}


/****************************************************************************
true if two netbios names are equal
****************************************************************************/
bool NbtUtil::name_equal(const char *s1, const char *s2)
{
  char *p1, *p2;
  while (*s1 && *s2 && (*s1!=' ') && (*s2!=' ')) 
  {
    p1 = const_cast<char*>(s1);
    p2 = const_cast<char*>(s2); /* toupper has side effects as a macro */
    if (toupper(*p1) != toupper(*p2))
      return(false);
    s1++;
    s2++;
  }
  return (true);
}



/****************************************************************************
interpret the weird netbios "name"
****************************************************************************/
void NbtUtil::name_interpret(const char *in,char *out)
{
    int len = (*in++) / 2;
    while (len--)
    {
        *out = ((in[0]-'A')<<4) + (in[1]-'A');
        in += 2;
        out++;
    }
    *out = 0;
    /* Handle any scope names */
    while(*in)
    {
      *out++ = '.'; /* Scope names are separated by periods */
      len = *(unsigned char *)in++;
      strncpy(out, in, len);
      out += len;
      *out=0;
      in += len;
    }
}


/****************************************************************************
mangle a name into netbios format
****************************************************************************/
void NbtUtil::name_mangle(char const *in,char *out)
{
  int len = strlen((char *)in);
  int pad = 0;

  if (len < 16)
    pad = 16 - len;

  //len <<= 1;

  //长度，最多不能超过255
  *out++ = (len + pad) << 1;
  while (*in)
    {
      out[0] = (in[0]>>4) + 'A';
      out[1] = (in[0] & 0xF) + 'A';
      in++;
      out+=2;
    }

  while (pad--)
    {
      out[0] = 'C';
      out[1] = 'A';
      out+=2;
    }

  *out = 0;
}

/****************************************************************************
return the total storage length of a mangled name
****************************************************************************/
int NbtUtil::name_len(const char *s)
{
  unsigned char const c = *(unsigned char const*)s;
  if ((c & 0xC0) == 0xC0)
    return(2);
  return(strlen(s) + 1);
}

/****************************************************************************
word out the length of a nmb message
****************************************************************************/
int NbtUtil::nmb_len(const char *buf)
{
    int i;
    int ret = 12;
    const char *p = buf;
    int qdcount = SVAL(buf,4);
    int ancount = SVAL(buf,6);
    int nscount = SVAL(buf,8);
    int arcount = SVAL(buf,10);

    for (i=0;i<qdcount;i++)
    {
        p = buf + ret;
        ret += name_len(p) + 4;
    }

    for (i=0;i<(ancount + nscount + arcount);i++)
    {
        int rdlength;
        p = buf + ret;
        ret += (name_len(p) + 8);
        p = buf + ret;
        rdlength = SVAL(p,0);
        ret += (rdlength + 2);
    }
   
    return(ret);
}


/****************************************************************************
show a nmb message
****************************************************************************/
void NbtUtil::show_nmb(const char *inbuf)
{
  int i,l;
  int name_trn_id = SVAL(inbuf,0);
  int opcode = (CVAL(inbuf,2) >> 3) & 0xF;
  int nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
  int rcode = CVAL(inbuf,3) & 0xF;
  int qdcount = SVAL(inbuf,4);
  int ancount = SVAL(inbuf,6);
  int nscount = SVAL(inbuf,8);
  int arcount = SVAL(inbuf,10);
  char name[100];

  Debug(2,"\nPACKET INTERPRETATION\n");

#if 0
  if (dbf)
    fwrite(inbuf,1,nmb_len(inbuf),dbf);
  Debug(0,"\n");
#endif
  //混淆了二进制与16进制
  if (opcode == 5 && ((nm_flags & ~1) == 0x10) && rcode == 0)
    Debug(2,"NAME REGISTRATION REQUEST (%s)\n",nm_flags&1?"Broadcast":"Unicast");

  if (opcode == 5 && ((nm_flags & ~1) == 0x00) && rcode == 0)
    Debug(2,"NAME OVERWRITE REQUEST AND DEMAND (%s)\n",nm_flags&1?"Broadcast":"Unicast");

  if (opcode == 9 && ((nm_flags & ~1) == 0x00) && rcode == 0)
    Debug(2,"NAME REFRESH REQUEST (%s)\n",nm_flags&1?"Broadcast":"Unicast");

  if (opcode == 5 && nm_flags == 0x58 && rcode == 0)
    Debug(2,"POSITIVE NAME REGISTRATION RESPONSE\n");

  if (opcode == 5 && nm_flags == 0x58 && rcode != 0 && rcode != 7)
    Debug(2,"NEGATIVE NAME REGISTRATION RESPONSE\n");

  if (opcode == 5 && nm_flags == 0x50 && rcode == 0)
    Debug(2,"END-NODE CHALLENGE REGISTRATION RESPONSE\n");

  if (opcode == 5 && nm_flags == 0x58 && rcode != 0 && rcode == 7)
    Debug(2,"NAME CONFLICT DEMAND\n");

  if (opcode == 6 && (nm_flags&~1) == 0x00 && rcode == 0)
    Debug(2,"NAME RELEASE REQUEST & DEMAND (%s)\n",nm_flags&1?"Broadcast":"Unicast");

  if (opcode == 6 && (nm_flags&~1) == 0x40 && rcode == 0)
    Debug(2,"POSITIVE NAME RELEASE RESPONSE\n");

  if (opcode == 6 && (nm_flags&~1) == 0x40 && rcode != 0)
    Debug(2,"NEGATIVE NAME RELEASE RESPONSE\n");

  if (opcode == 0 && (nm_flags&~1) == 0x10 && rcode == 0)
    Debug(2,"NAME QUERY REQUEST (%s)\n",nm_flags&1?"Broadcast":"Unicast");

  if (opcode == 0 && (nm_flags&~0x28) == 0x50 && rcode == 0)
    Debug(2,"POSITIVE NAME QUERY RESPONSE\n");

  if (opcode == 0 && (nm_flags&~0x08) == 0x50 && rcode != 0)
    Debug(2,"NEGATIVE NAME QUERY RESPONSE\n");

  if (opcode == 0 && nm_flags == 0x10 && rcode == 0)
    Debug(2,"REDIRECT NAME QUERY RESPONSE\n");

  if (opcode == 7 && nm_flags == 0x80 && rcode == 0)
    Debug(2,"WAIT FOR ACKNOWLEDGEMENT RESPONSE\n");

  if (opcode == 0 && (nm_flags&~1) == 0x00 && rcode == 0)
    Debug(2,"NODE STATUS REQUEST (%s)\n",nm_flags&1?"Broadcast":"Unicast");

  if (opcode == 0 && nm_flags == 0x80 && rcode == 0)
    Debug(2,"NODE STATUS RESPONSE\n");


  Debug(2,"name_trn_id=0x%x\nopcode=0x%x\nnm_flags=0x%x\nrcode=0x%x\n",
	name_trn_id,opcode,nm_flags,rcode);
  Debug(2,"qdcount=%d\nancount=%d\nnscount=%d\narcount=%d\n",
	qdcount,ancount,nscount,arcount);

  l = 12;
  for (i=0;i<qdcount;i++)
    {
      int type,class_code;
      Debug(2,"QUESTION %d\n",i);
      name_extract(inbuf,l,name);
      l += name_len(inbuf+l);
      type = SVAL(inbuf+l,0);
      class_code = SVAL(inbuf+l,2);
      l += 4;
      Debug(2,"\t%s\n\ttype=0x%x\n\tclass=0x%x\n",name,type,class_code);
    }

  for (i=0;i<(ancount + nscount + arcount);i++)
    {
      int type,class_code,ttl,rdlength;
      Debug(2,"RESOURCE %d\n",i);
      name_extract(inbuf,l,name);
      l += name_len(inbuf + l);
      type = SVAL(inbuf+l,0);
      class_code = SVAL(inbuf+l,2);
      ttl = IVAL(inbuf+l,4);
      rdlength = SVAL(inbuf+l,8);
      l += 10 + rdlength;
      Debug(2,"\t%s\n\ttype=0x%x\n\tclass=0x%x\n",name,type,class_code);
      Debug(2,"\tttl=%d\n\trdlength=%d\n",ttl,rdlength);
    }

  Debug(2,"\n");
}


/****************************************************************************
send a single packet to a port on another machine
****************************************************************************/
bool NbtUtil::send_packet(const char *buf,int len,struct in_addr *ip,int port,int type)
{
  bool ret;
  int out_fd;
  struct sockaddr_in sock_out;


  //log_out(buf,len);

  if (passive)
    return(true);

  /* create a socket to write to */
  out_fd = socket(AF_INET, type, 0);
  if (out_fd == -1)
  {
      Debug(0,"socket failed");
      return false;
   }
#if 1
  /* allow broadcasts on it */
  int one=1;

  setsockopt(out_fd,SOL_SOCKET,SO_BROADCAST,&one,sizeof(one));
#endif

  /* set the address and port */
  memset(&sock_out, 0, sizeof(sock_out));
  memcpy(&sock_out.sin_addr, ip, 4);
  sock_out.sin_port = htons( port );
  sock_out.sin_family = AF_INET;

  /* log the packet */
  //log_out(buf,len);


  Debug(3,"sending a packet of len %d to (%s) on port %d of type %s\n",
	len,inet_ntoa(*ip),port,type==SOCK_DGRAM?"DGRAM":"STREAM");

  /* send it */
  ret = (sendto(out_fd,buf,len,0,(struct sockaddr *)&sock_out,sizeof(sock_out)) >= 0);

  if (!ret)
    Debug(0,"Send packet failed for \n", 
            str_socket_error(get_socket_error(out_fd)));

  closesocket(out_fd);
  return(ret);
}

/****************************************************************************
send a name message
****************************************************************************/
#if 0
bool NbtUtil::send_nmb(int fd, const char *buffer)
{
  int len;
  int ret,nwritten=0;
  len = nmb_len(buffer);

  while (nwritten < len)
  {
      ret = write_socket(fd,buffer+nwritten,len - nwritten);
      if (ret <= 0)
	  {
          Debug(0,"Error writing %d bytes to client. %d. Exiting\n",len,ret);
          return(false);
	  }
      nwritten += ret;
  }

  //log_out(buffer,len);

  return(true);
}
#endif

/****************************************************************************
receive a name message
****************************************************************************/
bool NbtUtil::receive_nmb(socket_t client, char *buffer,
        int bufsize, struct in_addr *ip_addr, int timeout)
{
  int ret = read_max_data(client,buffer,
          bufsize,ip_addr,timeout);

    if (ret < 0)
    {
      Debug(0,"No bytes from client\n");
      closesocket(client);
      exit(0);
    }

    if (ret == 0)
      return false;

  //log_in(buffer,ret);

     Debug(2,"nmb_len=%d len=%d\n",nmb_len(buffer),ret);

    return(true);
}
/****************************************************************************
read data from the client. Maxtime is in 10ths of a sec
****************************************************************************/
int NbtUtil::read_max_data(int fd,char *buffer,int bufsize,struct in_addr *ipaddr, int maxtime)
{
  fd_set fds;
  int   selrtn;
  int  nready;
  int nread;
  struct timeval timeout;

  do {
      FD_ZERO(&fds);
      FD_SET(fd,&fds);

      timeout.tv_sec = maxtime;
      timeout.tv_usec = 0;
      //Debug(2, "wait for %d seconds\n", maxtime);
      do {
          if (maxtime > 0)
              selrtn = select(fd+1,&fds,NULL,NULL,&timeout);
          else
              selrtn = select(fd+1,&fds,NULL,NULL,NULL);
      } while( selrtn < 0  &&  errno == ERR(EINTR));

      if (selrtn < 0)
      {
          Debug(2, "select failed for %s\n", str_socket_error(socket_error()));
          return -1;
      }

      if (!FD_ISSET(fd,&fds))
          return 0;

      //Debug(2, "the socket become readable\n");

      /* Query the system how many bytes are ready to be read */
      ioctl(fd, FIONREAD, &nready);

      /* Only try to get the smaller of nready and BUFSIZE */
      nread = read_socket(fd, buffer, nready < bufsize ? nready : bufsize, ipaddr);
      
  } while (0);
  /* return the number got */
  return(nread);
}



/****************************************************************************
read from a socket
****************************************************************************/
int NbtUtil::read_socket(int fd,char *buf,int len, struct in_addr *ipaddr)
{
  /* #define NORECVFROM */
#ifdef NORECVFROM
  return (read(fd,buf,len));
#else
  int ret;
  struct sockaddr sock;
  socklen_t socklen;
  struct in_addr lastip;

  socklen = sizeof(sock);
  memset(&sock, 0, socklen);
  memset(&lastip, 0, sizeof(lastip));
  ret = recvfrom(fd,buf,len,0,&sock,&socklen);
  if (ret <= 0)
    Debug(0,"read socket failed. ERRNO=%d\n",errno);
  else
    {
      lastip = *(struct in_addr *) &sock.sa_data[2];
      if (ipaddr)
        *ipaddr = *(struct in_addr *) &sock.sa_data[2];
      Debug(3,"read %d bytes from (%s)\n",ret,inet_ntoa(lastip));
    }
  return(ret);
#endif
}

/****************************************************************************
write to a socket
****************************************************************************/
int NbtUtil::write_socket(int fd,char *buf,int len)
{
 if (passive)
  return(len);
 return(write(fd,buf,len));
}

/****************************************************************************
get the broadcast address for our address (troyer@saifr00.ateng.az.honeywell.com)
****************************************************************************/
bool NbtUtil::broadcast_ip(struct in_addr *if_ipaddr, struct in_addr *if_bcast)
{
#if 0
  int sock = -1;               /* AF_INET raw socket desc */
  char buff[1024];
  struct ifconf ifc;
  struct ifreq *ifr;
  int i;

  /* Create a socket to the INET kernel. */
#ifdef sun386
  if ((sock = socket(AF_INET, SOCK_DGRAM, 0 )) < 0)
#else
  if ((sock = socket(AF_INET, SOCK_RAW, PF_INET )) < 0)
#endif
    {
      Debug(0, "Unable to open socket to get broadcast address\n");
      return(false);
    }

  /* Get a list of the configures interfaces */
  ifc.ifc_len = sizeof(buff);
  ifc.ifc_buf = buff;
  if (ioctl(sock, SIOCGIFCONF, &ifc) < 0)
  {
      Debug(0, "SIOCGIFCONF: %s\n", strerror(errno));
      return(false);
  }

  /* Loop through interfaces, looking for given IP address */
  ifr = ifc.ifc_req;
  for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++)
  {
      Debug(3,"Interface: %s  IP addr: %s\n", ifr->ifr_name,
	    inet_ntoa((*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr));
      if (if_ipaddr->s_addr == (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr)
        break;
  }
  if (i < 0)
  {
      Debug(0,"No interface found for address %s\n", inet_ntoa(*if_ipaddr));
      return(false);
  }

  /* Get the broadcast address from the kernel */
  if (ioctl(sock, SIOCGIFBRDADDR, ifr) < 0)
  {
      Debug(0,"SIOCGIFBRDADDR failed\n");
      return(false);
  }

  memcpy(if_bcast,&(*(struct sockaddr_in *) &ifr->ifr_broadaddr).sin_addr,4);

  /* Close up shop */
  (void) close_socket(sock);

  Debug(2,"Broadcast address for %s = %s\n", ifr->ifr_name,
	inet_ntoa(*if_bcast));
  return(true);
#endif
  return true;
}


int NbtUtil::get_ipaddr(struct sockaddr_in *ip_addr, struct sockaddr_in *bcast_addr)
{
  //char addr_str[128];
  struct ifaddrs *ifaddr_ptr;
  int res = getifaddrs(&ifaddr_ptr);
  if (res != 0)
  {
    Debug(2, "get the ifaddr failed\n" );
    return -1;
  }

  for (; ifaddr_ptr != NULL; ifaddr_ptr = ifaddr_ptr->ifa_next)
  {
    if (ifaddr_ptr->ifa_addr->sa_family == AF_INET)
    {
      Debug(3, "interface name:%s\n", ifaddr_ptr->ifa_name); 
      if (strcmp(ifaddr_ptr->ifa_name, "lo") == 0)
          continue;
      struct sockaddr_in *pAddr = (struct sockaddr_in*)(ifaddr_ptr->ifa_addr);
      //inet_ntop(AF_INET, &(pAddr->sin_addr), addr_str, sizeof(addr_str)); 
      //printf ( "ip addr:%s\n", addr_str );
      if (pAddr->sin_addr.s_addr !=  INADDR_LOOPBACK)
      {
        memcpy(ip_addr, pAddr, sizeof(struct sockaddr_in));
      } 
      else
          continue;
//#ifdef SIOCGIFBRDADDR
      if (ifaddr_ptr->ifa_flags & IFF_BROADCAST)
      {
        memcpy(bcast_addr, ifaddr_ptr->ifa_broadaddr, sizeof(struct sockaddr_in));
        break;
        //inet_ntop(AF_INET, &bcast_addr->sin_addr, addr_str, sizeof(addr_str));
        //printf ( "the brocast addr:%s\n", addr_str);
      }
//#endif
    } // family is inet
  } //end of for

  //freeifaddrs(ifaddr_ptr);
  return 0;
}

int NbtUtil::get_macaddr(char *buf, int buflen)
{
  static char if_buf[1024] = {'\0'}; 
  
  struct ifconf ifcon;
  struct ifreq *ifr = NULL;

  int sockfd = -1;

  ifcon.ifc_buf = if_buf;
  ifcon.ifc_len = sizeof(if_buf);

  sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (sockfd == -1)
  {
    Debug(2, "create the socket failed\n" );
    return -1;
  }

  int res = ioctl(sockfd, SIOCGIFCONF, &ifcon);
  if (res == 0)
  {
      int idx_cnt = ifcon.ifc_len / sizeof(struct ifreq);
      
      int k = 0;
      ifr = ifcon.ifc_req;
      for (; k < idx_cnt; ++k, ++ifr)
      {
        Debug(3, "Interface name:%s\n", ifr->ifr_name);
        if (strcmp(ifr->ifr_name, "lo") == 0)
          continue;

        if (!ioctl(sockfd, SIOCGIFHWADDR, ifr))
        {
          //print_hex((char*)(ifr->ifr_hwaddr.sa_data), 6);
          memcpy(buf, (char*)(ifr->ifr_hwaddr.sa_data), 6);
          break;
          //printf ( "the mac address is:%s\n", buf);
        }
      } //for
  } //ioctl
  return 0;
}
